整理代码
因为生成HTML的代码会被重复执行很多次,所以我们决定对这些代码进行一些整理,并将它们移到代码清单2-10所示的 generateHTML
函数里面。
代码清单2-10 generateHTML
函数
func generateHTML(w http.ResponseWriter, data interface{}, fn ...string) {
var files []string
for _, file := range fn {
files = append(files, fmt.Sprintf("templates/%s.html", file))
}
templates := template.Must(template.ParseFiles(files...))
templates.ExecuteTemplate(writer, "layout", data)
}
generateHTML
函数接受一个 ResponseWriter
、一些数据以及一系列模板文件作为参数,然后对给定的模板文件进行语法分析。 data
参数的类型为空接口类型(empty interface type),这意味着该参数可以接受任何类型的值作为输入。刚开始接触Go语言的人可能会觉得奇怪——Go不是静态编程语言吗,它为什么能够使用没有类型限制的参数?
但实际上,Go程序可以通过接口(interface)机制,巧妙地绕过静态编程语言的限制,并借此获得接受多种不同类型输入的能力。Go语言中的接口由一系列方法构成,并且每个接口就是一种类型。一个空接口就是一个空集合,这意味着任何类型都可以成为一个空接口,也就是说任何类型的值都可以传递给函数作为参数。
generateHTML
函数的最后一个参数以3个点( ...
)开头,它表示 generateHTML
函数是一个可变参数函数(variadic function),这意味着这个函数可以在最后的可变参数中接受零个或任意多个值作为参数。 generateHTML
函数对可变参数的支持使我们可以同时将任意多个模板文件传递给该函数。在Go语言里面,可变参数必须是可变参数函数的最后一个参数。
在实现了 generateHTML
函数之后,让我们回过头来,继续对 index
处理器函数进行整理。代码清单2-11展示了经过整理之后的 index
处理器函数,现在它看上去更整洁了。
代码清单2-11 index
处理器函数的最终版本
func index(writer http.ResponseWriter, request *http.Request) {
threads, err := data.Threads(); if err == nil {
_, err := session(writer, request)
if err != nil {
generateHTML(writer, threads, "layout", "public.navbar", "index")
} else {
generateHTML(writer, threads, "layout", "private.navbar", "index")
}
}
}
在这一节中,我们学习了很多关于模板的基础知识,之后的第5章将对模板做更详细的介绍。但是在此之前,让我们先来了解一下ChitChat应用使用的数据源(data source),并借此了解一下ChitChat应用的数据是如何与模板一同生成最终的HTML的。